<?php
/**
 * SYNC_CLIENT.class.php
 *
 * @desc Sync client class
 * @author Andrew lee <andrew(at)dianboom(dot)com>
 * @package client_api
 * @since 16:28 2011/08/19
 * @copyright 2010-2011 dianboom corporation. All rights reserved.
 */

class SYNC_CLIENT {

    /**
     * @access protected
     */
    protected


        /**
         * @var $_method
         *
         * @desc method name
         */
        $_method = NULL,

        /**
         * @var $_config
         *
         * @desc Client configure
         */
        $_config = NULL,

        /**
         * @var $_dbObj
         *
         * @desc database class object
         */
        $_dbObj = NULL,

        /**
         * @var $_dbPrefix
         *
         * @desc table prefix string
         */
        $_dbPrefix = NULL,

        /**
         * @var $_dbMapping
         *
         * @desc table fields mapping
         */
        $_dbMapping = NULL;


    /**
     * @method __construct
     *
     * @desc the construct method
     * @access public
     * @param array $config
     * @return void
     */
    public function __construct($config, $method) {

        /**
         * @desc check class implements
         */
        $this->checkImplements();

        /**
         * @desc initialize configure
         */
        $this->_config = $config;

        /**
         * @desc validate POST data
         */
        $this->checkPOST();

        /**
         * @desc check method parameters
         */
        $this->checkParams($method);

        /**
         * @desc set _method
         */
        $this->_method = $method;

        /**
         * @desc initialize database object
         */
        $this->initDB();
    }


    /**
     * @method run
     *
     * @desc run class method
     * @access public
     * @return void
     */
    public function run() {
        try {
            call_user_func_array(array($this, $this->_method), $_POST);
        } catch (Exception $e) {
            $this->display($e->getMessage(), -1);
        }
    }


    /**
     * @method token
     *
     * @desc get the token from configure
     * @access public
     * @return void
     */
    public function token() {
        $this->display('TRUE');
    }


    /**
     * @method initDB
     *
     * @desc initialize db object & configure
     * @return void
     */
    private function initDB() {

        require INCLUDE_ROOT. 'adodb_lite'. DIRECTORY_SEPARATOR. 'adodb.inc.php';

        $dsn = $this->_config['db']['db_type']. '://'.
            $this->_config['db']['db_username']. ':'.
            rawurlencode($this->_config['db']['db_password']). '@'.
            $this->_config['db']['db_host']. '/'.
            $this->_config['db']['db_name']. '?persist='.
            $this->_config['db']['db_pconnect']. '&charset='.
            $this->_config['db']['db_charset'];

        $this->_dbObj = NewADOConnection($dsn);

        $this->_dbObj || $this->display('Database connecting failed');

        $this->_dbObj->setDBcharset($this->_config['db']['db_charset']);

        $this->_dbPrefix = $this->_config['db']['db_prefix'];

        if (is_array($this->_config['methodFieldMapping']) && count($this->_config['methodFieldMapping']) > 0)
            $this->_dbMapping = $this->_config['methodFieldMapping'];
        else {
            file_exists($fieldFile = INCLUDE_ROOT. 'field_mappings'. DIRECTORY_SEPARATOR. $this->_config['system']. '.php') || $this->display('No method field mapping');

            $this->_dbMapping = require $fieldFile;
        }

    }


    /**
     * @method checkImplements
     *
     * @desc check implements
     * @return void
     */
    protected function checkImplements() {
        in_array('SYNC_CLIENT_INT', class_implements($this)) ||
            $this->display('The class is not instance of SYNC_CLIENT_INT', -1);
    }


    /**
     * @method genFieldsQuery
     *
     * @desc generate fields query by fields mapping
     * @access protected
     * @param string $table
     * @return string $sql
     */
    protected function genFieldsQuery($table) {
        isset($this->_dbMapping[$table]) || $this->display('Unknown table for selecting');

        $sql = '';

        /**
         * @desc get fields
         */
        $arr = $this->_dbObj->getArray("SHOW fields FROM `{$this->_dbPrefix}{$this->_dbMapping[$table]['table']}`");

        $fields = array();

        foreach ($arr as $f) {
            $fields[] = $f['Field'];
        }

        foreach($this->_dbMapping[$table] as $targetField=>$sourceField)
            if ('table' != $targetField)
                if (preg_match('/^[a-z_]+$/i', $sourceField) && !in_array($sourceField, $fields))
                    $this->display("The field [$sourceField] not exists", -1);
                else
                    if (NULL === $sourceField)
						$sql .= ($sql? ', ': ''). "NULL AS `$targetField`";
					elseif (is_numeric($sourceField) || preg_match('/\W/', $sourceField))
                        $sql .= ($sql? ', ': ''). "$sourceField AS `$targetField`";
                    else
                        $sql .= ($sql? ', ': ''). "`$sourceField` AS `$targetField`";

        $sql || $this->display('Unknown fields for selecting', -1);

        return str_replace('@__', $this->_dbPrefix, $sql);
    }


    /**
     * @method display
     *
     * @desc format the output data and output
     * @access protected
     * @param mixed $data
     * @param integer $status default 1
     * @param boolen return default FALSE
     * @return mixed
     */
    protected function display($data, $status = 1, $return = FALSE) {
        header("Content-type:{$this->_config['outputType']}; charset={$this->_config['charset']}");

        /**
         * @desc message status
         */
        $statuses = array(
            -1, // Fatal error
            0, // FALSE
            1 // TRUE
        );

        is_int($status) && in_array($status, $statuses) || $this->display($data, 0, $return);

        $string = '';

        switch($this->_config['outputType']) {
        case 'text/html':
            $string = var_export($data, TRUE);
            break;

        case 'application/json':
            $string = json_encode(array($status, $data));
            break;

        case 'application/xml':
            $string = '';
            break;
        }

        if (FALSE === $return) {
            echo $string;
            exit;
        }

        return $string;
    }


    /**
     * @method makedir
     *
     * @desc Make dirs
     * @since 11:44 2012/02/21
     * @access protected
     * @return boolean
     */
    protected function makedir($dir) {
        return is_dir($dir) || $this->makedir(dirname($dir)) && mkdir($dir);
    }


    /**
     * @method checkPOST
     *
     * @desc check post data
     * @access private
     * @return void
     */
    private function checkPOST() {
        isset($_POST['key']) && ($key = $_POST['key']) || $this->display('Key empty', -1);

        unset($_POST['key']);

        ksort($_POST);

        $key == md5(
            $this->_config['email']. '|'.
            $this->_config['token']. '|'.
            http_build_query($_POST, '', '&')
        ) || $this->display('Invalid key', -1);
    }


    /**
     * @method checkParams
     *
     * @desc check post parameters
     * @access private
     * @param string $method
     * @return void
     */
    private function checkParams($method) {
        $classMethod = new ReflectionMethod($this, $method);
        $params = $classMethod->getParameters();

        $useParams = array();
        foreach($params as $param)
            isset($_POST[$param->getName()]) &&
            1+($useParams[$param->getName()] = $_POST[$param->getName()]) ||
            $param->isOptional() ||
            $this->display("Invalid parameter {$param->getName()}", -1);

        $_POST = $useParams;
    }
}
?>
